#include "FileListViewCtrl.h"
#include "xDocument.h"
#include "MainWindow.h"

static const int COLUMN_WIDTH = 100;

int CFileListViewCtrl::_GetIndexById(FileInfo::ID id) {
	ATLASSERT (id != FileInfo::INVALID_ID);
	int count = GetItemCount();
	for (int i = 0; i < count; ++ i) {
		LVITEM li;
		li.mask = LVIF_PARAM;
		li.iItem = i;
		GetItem(&li);
		if (li.lParam == id) {
			return i;
		}
	}

	return -1;
}


CFileListViewCtrl::CFileListViewCtrl(IFileListViewCtrlParent *pWndParent)
	: m_pWndParent(pWndParent)
{
	ATLASSERT (m_pWndParent != NULL);
	m_sysImgList.load();

	CxDocument *pDoc = m_pWndParent->getDocument();
	ATLASSERT (pDoc);
	pDoc->subscribe(this);
}

CFileListViewCtrl::~CFileListViewCtrl(void) {
	ATLASSERT (m_pWndParent != NULL);
	CxDocument *pDoc = m_pWndParent->getDocument();
	ATLASSERT (pDoc);
	pDoc->unsubscribe(this);
}

HWND CFileListViewCtrl::createWindow() {
	HWND hWnd;

	// file list
	DWORD dwStyle = WS_CHILD | WS_VISIBLE | LVS_REPORT;// | WS_BORDER;
	if (1) {
		dwStyle |= LVS_OWNERDRAWFIXED;
	}
	hWnd = Create(
	              m_pWndParent->getFileListViewCtrlParentHwnd(), 
	              m_pWndParent->getFileListViewCtrlRect(),
	              NULL, 
	              dwStyle,
	              WS_EX_CLIENTEDGE,
	              m_pWndParent->getFileListViewCtrlId()
	             );
	if (hWnd == NULL) {
		return NULL;
	}

	DWORD dwExStyle = LVS_EX_FULLROWSELECT | LVS_EX_DOUBLEBUFFER | LVS_EX_BORDERSELECT
	                  | LVS_EX_LABELTIP | LVS_EX_INFOTIP;
	SetExtendedListViewStyle(dwExStyle);

	ATLASSERT (m_sysImgList.getImageList() != NULL);
	SetImageList(m_sysImgList.getImageList(), LVSIL_SMALL);

	LVCOLUMN lvc;
	lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
	lvc.fmt = LVCFMT_LEFT | LVCFMT_FIXED_WIDTH;
	lvc.cx = 100;

	lvc.pszText = _T("File");
	InsertColumn(0, &lvc);

	lvc.fmt |= LVCFMT_RIGHT;
	lvc.cx = COLUMN_WIDTH;
	lvc.pszText = _T("Size");
	InsertColumn(1, &lvc);

	lvc.cx = COLUMN_WIDTH;
	lvc.pszText = _T("Progress");
	InsertColumn(2, &lvc);

	adjustColumnWidth();



	return hWnd;
}

void CFileListViewCtrl::adjustColumnWidth() {
	ATLASSERT (IsWindow());

// 	if (GetColumnWidth(1) != COLUMN_WIDTH || GetColumnWidth(2) != COLUMN_WIDTH) {
// 		return; // user has adjusted it, we don't do it
// 	}

	CRect rc;
	GetClientRect(rc);
	rc.right -= ::GetSystemMetrics(SM_CXVSCROLL);
	int width[] = {
		rc.Width() - COLUMN_WIDTH - COLUMN_WIDTH,
		COLUMN_WIDTH,
		COLUMN_WIDTH,
	};

	for (int i = 0; i < COUNT_OF(width); ++ i) {
		SetColumnWidth(i, width[i]);
	}
}

bool CFileListViewCtrl::insertFile (const FileInfo &fi) {

	int index = GetItemCount();
	int imageIndex = m_sysImgList.getIconIndexByName(fi.pathname);

	LVITEM li;
	memset (&li, 0, sizeof(li));
	li.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
	li.pszText = (TCHAR *)fi.pathname.c_str();
	li.iItem = index;
	li.iImage = imageIndex;
	li.lParam = fi.id;
	InsertItem(&li);

	double size = fi.size;
	TCHAR buf[20];
	if (fi.size < 1000 * 1000) {//1024 * 1024) {
		_stprintf_s (buf, 20, _T("%.2fK"), size / 1024);
	} else {
		_stprintf_s (buf, 20, _T("%.2fM"), size / (1024 * 1024));
	}

	SetItemText(index, 1, buf);

	SetItemText(index, 2, _T(""));

	return true;
}

void CFileListViewCtrl::updateFile (FileInfo::ID id) {

	int index = _GetIndexById(id);
	ATLASSERT (index != -1);

	CxDocument *pDoc = m_pWndParent->getDocument();
	CxDocument::INDEX docIndex = pDoc->getIndexById(id);
	ATLASSERT (docIndex != CxDocument::INVALID_INDEX);
	const FileInfo &fi = pDoc->getFileByIndex(docIndex);

	if (fi.status == FileInfo::US_UPLOADING) {
		TCHAR buf[80];
		_stprintf_s(buf, 80, _T("%d%%"), fi.progress);
		SetItemText(index, 2, buf);
	} else if (fi.status == FileInfo::US_OK) {
		SetItemText(index, 2, _T("OK"));
	} else if (fi.status == FileInfo::US_FAILED) {
		SetItemText(index, 2, _T("Failed"));
	} else {
		SetItemText(index, 2, _T(""));
	}
}

FileInfo::ID CFileListViewCtrl::getIdFromItem(int iItem) {
	FileInfo::ID id = FileInfo::INVALID_ID;

	LVITEM lvi;
	memset (&lvi, 0, sizeof(lvi));
	lvi.mask = LVIF_PARAM;
	lvi.iItem = iItem;
	if (GetItem(&lvi)) {
		id = lvi.lParam;
	}
	return id;
}

void CFileListViewCtrl::clear() {
	this->DeleteAllItems();
}

void CFileListViewCtrl::drawItem (DRAWITEMSTRUCT *pdis) {
	ATLASSERT (pdis->CtlType == ODT_LISTVIEW);
	ATLASSERT (pdis->CtlID == m_pWndParent->getFileListViewCtrlId());
	int cx, cy;
	ImageList_GetIconSize(m_sysImgList.getImageList(), &cx, &cy);

	CRect rcItem = pdis->rcItem;

	CDCHandle dc(pdis->hDC);
	COLORREF rgbBg = (pdis->itemState & ODS_SELECTED) ? ::GetSysColor(COLOR_HIGHLIGHT) : ::GetSysColor(COLOR_WINDOW);
	rcItem.left += cx;
	dc.FillSolidRect(rcItem, rgbBg);

	int index = pdis->itemID; // MapIDToIndex(pdis->itemID);
	CRect rc;

	TCHAR buf[MAX_PATH];
	LVITEM lvi;
	memset (&lvi, 0, sizeof(lvi));
	lvi.mask = LVIF_IMAGE | LVIF_STATE | LVIF_TEXT | LVIF_PARAM;
	lvi.iItem = index;
	lvi.stateMask = (UINT)-1;
	lvi.pszText = buf;
	lvi.cchTextMax = MAX_PATH;
	this->GetItem(&lvi);

	FileInfo::ID id = lvi.lParam;
	CxDocument *pDoc = m_pWndParent->getDocument();
	CxDocument::INDEX docIndex = pDoc->getIndexById(id);
	ATLASSERT (docIndex != CxDocument::INVALID_INDEX);
	const FileInfo &fi = pDoc->getFileByIndex(docIndex);

	bool selected = (lvi.state & LVIS_SELECTED) ? true : false;
	COLORREF rgbText = selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : GetSysColor(COLOR_WINDOWTEXT);
	COLORREF rgbOld = dc.SetTextColor(rgbText);

	// draw text
	if (GetSubItemRect(index, 1, LVIR_BOUNDS, rc)) {
		rcItem = pdis->rcItem;
		ImageList_Draw(m_sysImgList.getImageList(), lvi.iImage, dc, 
		               rcItem.left,
		               rcItem.top + (rcItem.Height() - cy) / 2,
		               ILD_NORMAL
		              );

		rcItem.left = cx + 2;
		rcItem.right = rc.left - 2;

		DWORD drawFlags = DT_LEFT | DT_PATH_ELLIPSIS | DT_VCENTER | DT_SINGLELINE;
		dc.DrawText(lvi.pszText, -1, rcItem, drawFlags);

		// draw size
		rcItem = rc;
		rcItem.left += 2;
		rcItem.right -= 2;
		GetItemText(index, 1, buf, MAX_PATH);
		dc.DrawText(buf, -1, rcItem, DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
	}
	dc.SetTextColor(rgbOld);

	// draw progress
	if (GetSubItemRect(index, 2, LVIR_BOUNDS, rc)) {
		GetItemText(index, 2, buf, MAX_PATH);

		DWORD dwFlags = DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS;
		rcItem = rc;
		rcItem.left += 2;
		rcItem.right -= 2;

		rgbText = selected ? RGB(127,127,127) : GetSysColor(COLOR_WINDOWTEXT);
		COLORREF rgbBar = RGB(0,192,0);
		CRect rcBar = rcItem;
		rcBar.top += 2;
		rcBar.bottom -= 2;
		if (fi.status == FileInfo::US_UPLOADING) {
			dc.Rectangle(rcBar);
			rcBar.DeflateRect(1,1,1,1);
			int width = (int)((fi.progress * rcBar.Width()) / 100);
			rcBar.right = rcBar.left + width;
			dc.FillSolidRect(rcBar, rgbBar);
		} else if (fi.status == FileInfo::US_OK) {
// 			dc.Rectangle(rcBar);
// 			rcBar.DeflateRect(1,1,1,1);
// 			dc.FillSolidRect(rcBar, rgbBar);
			rgbText = selected ? GetSysColor(COLOR_HIGHLIGHTTEXT) : RGB(0,127,0);
		} else if (fi.status == FileInfo::US_FAILED) {
			rgbText = RGB(255,0,0);
			_tcscpy_s(buf, MAX_PATH, _T("failed"));
		}

		rgbOld = dc.SetTextColor(rgbText);
		dc.DrawText(buf, -1, rcItem, dwFlags);
		dc.SetTextColor(rgbOld);
	}

}

void CFileListViewCtrl::measureItem (WPARAM wParam, LPARAM lParam) {
	MEASUREITEMSTRUCT *pmis = (MEASUREITEMSTRUCT *)lParam;
	ATLASSERT (pmis != NULL);
	ATLASSERT (pmis->CtlID == m_pWndParent->getFileListViewCtrlId());
	ATLASSERT (pmis->CtlType == ODT_LISTVIEW);

	DefWindowProc(m_hWnd, WM_MEASUREITEM, wParam, lParam);
}

void CFileListViewCtrl::onEvent(CxDocument::IObserver::EVT evt, void *param) {
	ATLASSERT (m_pWndParent);
	CxDocument *pDoc = m_pWndParent->getDocument();
	ATLASSERT (pDoc);
	if (evt == CxDocument::EVT_ADD) {
		FileInfo::ID id = *(FileInfo::ID *)param;
		CxDocument::INDEX docIndex = pDoc->getIndexById(id);
		ATLASSERT (docIndex != CxDocument::INVALID_INDEX);
		const FileInfo &fi = pDoc->getFileByIndex(docIndex);
		if (!insertFile(fi)) {
			ATLASSERT (false);
		}
	} else if (evt == CxDocument::EVT_REMOVE) {
		FileInfo *pfi = (FileInfo *)param;
		int index = _GetIndexById(pfi->id);
		ATLASSERT (index != -1);
		DeleteItem(index);
	} else if (evt == CxDocument::EVT_CLEAR) {
		clear();
	} else if (evt == CxDocument::EVT_UPDATE) {
		FileInfo::ID id = *(FileInfo::ID *)param;
		// updateFile(id);
		HWND hWndParent = m_pWndParent->getFileListViewCtrlParentHwnd();
		if (::IsWindow(hWndParent)) {
			::PostMessage(hWndParent, WM_APP_UPDATE_LISTVIEW, (WPARAM)id, 0);
		}
	} else {
		ATLASSERT (false);
	}
}